package Triangulation;

import java.util.List;

import controller.Globals;
import controller.Notification;
import controller.NotificationCenter;
import tools.FastLMTSkeletonCreator;
import Triangulation.minimalityMetrics.MinimalityMetric;
import datatypes.Edge;
import datatypes.FaceSet;
import datatypes.Vertex;

/**
 * 
 * @author markessien, nils
 *
 */
public class FastLMTTriangulator implements LMTTriangulator
{
	FastLMTSkeletonCreator lmtSkeletonCreator = null;
	private boolean betaSkeletonEnabled;
	
	public FastLMTTriangulator (boolean betaSkeletonEnabled) {
		this.betaSkeletonEnabled = betaSkeletonEnabled;
	}
	
	public void abort() {
		if (lmtSkeletonCreator != null) {
			lmtSkeletonCreator.abort();
		}
	}
	
	public void restartWithVertices(Vertex[] vertices, MinimalityMetric metric, boolean triangulatePolygons) {
		abort();
		processVertices(vertices, metric, triangulatePolygons);
	}
	
	@Override
	public List<Edge> processVertices(Vertex[] vertices, MinimalityMetric metric, boolean triangulatePolygons)
	{
		NotificationCenter nc = NotificationCenter.defaultNotificationCenter();
        nc.postNotification(Notification.willStartProcessing, this);
		this.lmtSkeletonCreator = new FastLMTSkeletonCreator(metric, (betaSkeletonEnabled && metric.betaSkeletonApplicable()));
		long startSet = System.currentTimeMillis();
		Vertex[] verticesClone = new Vertex[vertices.length];
		for (int i = 0; i < vertices.length; i++) {
			verticesClone[i] = vertices[i].clone();
		}
		if (Globals.USE_EXPLICIT_GARBAGE_COLLECTION) {
			Runtime r = Runtime.getRuntime();
			r.gc();
		}
		FaceSet set = new FaceSet(metric.getAngleForDiamondProperty(), (betaSkeletonEnabled && metric.betaSkeletonApplicable()), verticesClone);
		double timeGenerateSet = ((double)(System.currentTimeMillis() - startSet))/1000;
		
		System.out.println("Running time adding vertices to set: " + timeGenerateSet);
		
		// Calculate the LMT
		FaceSet returnedSet = lmtSkeletonCreator.calculateLMT(set);
		
		// now you can get the edges of the LMT from the set's edgesIn list.
		
		// n.b.: set.getCandidateEdges() will return a list of all locally minimal edges
		// that are left over, i.e. those edges are only within the holes to fill.
		// You may want to use those to quickly identify the vertices forming the holes.
		
		if (returnedSet != null) {
			nc.postNotification(Notification.didProcessXPercent, 90);
			// otherwise, the triangulation has been aborted and there wouldn't be any need to triangulate polygons.
			if (triangulatePolygons) {
				//fill the holes!
				new PolygonTriangulator().triangulateLMTHoles(returnedSet, metric);
				returnedSet.edgesIn.addAll(0, returnedSet.edgesLMTHole);
			}

			// just for grins, showing edges failing diamond test:
//					for (Edge edge : set.edgesFailedDiamondTest) {
//						set.edgesIn.add(edge);
//					}

			// just for grins, showing leftover candidate edges:
			returnedSet.edgesIn.addAll(0, returnedSet.getCandidateEdges());
//			if (returnedSet.getCandidateEdges().size() > 3) {
//				for (Edge edge : returnedSet.getCandidateEdges()) {
//					returnedSet.edgesIn.add(0, edge);
//				}	//produces infinite loop with 3 vertices!
//			}


//			NotificationCenter nc = NotificationCenter.defaultNotificationCenter();
			nc.postNotification(Notification.didFinishMWT, returnedSet.edgesIn);
		}
		return returnedSet.edgesIn;
	}

	
	@Override
	public String getName()
	{
		if (betaSkeletonEnabled) {
			return "beta LMT";
		}
		return "LMT";
	}
	
	public FastLMTSkeletonCreator getSkeletonCreator() {
		return lmtSkeletonCreator;
	}

	@Override
	public List<Edge> processVertices(Vertex[] points, MinimalityMetric metric) {
		return processVertices(points, metric, true);
	}
		
}